package se392.ateam2006.utils;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.GregorianCalendar;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import se392.ateam2006.resourcemgmt.participant.ParticipationEntity;
import se392.ateam2006.exceptions.MailException;
import se392.ateam2006.exceptions.InvalidDayException;


/**
 * Send emails using the javax.mail API. This is used to notify users when they have been
 *      added or removed from meetings
 * @author Ateam (Matthew Bennett, Claire Melton, Shingai Manyiwa, John Adderley)
 * @version 25/03/07
 */
public class EmailSender {
    
    private static final String DATE_HEADER_PATTERN = "EEE, d MMM yyyy H:m:s Z";
    private final String X_MAILER_HEADER = "Group B - Room Booking System";
    public final MimeMessage message;
    
    private ParticipationEntity[] participation;
    private String userMessage;
    
    private String smtpServer;
    
    private static String MONDAY = "monday";
    private static String TUESDAY = "tuesday";
    private static String WEDNESDAY = "wednesday";
    private static String THURSDAY = "thursday";
    private static String FRIDAY = "friday";
    private static String SATURDAY = "saturday";
    private static String SUNDAY = "sunday";
    private static Date systemClock = new Date();
    
    // If we're testing we don't want to send emails (or we'll get spammed!)
    public static boolean isTesting = false;
    
    /**
     * Set the date
     * @param clock - the Date to set to
     */
    public static void setDate(Date clock){
        systemClock = clock;
    }
    
    /**
     * Get the date
     * @return Date - the current date that we have stored
     */
    public static Date getClock(){
        return systemClock;
    }
    
    /**
     * Build a string containing a list of the day numbers
     * @param d - a string containg a list of days
     * @return String - a string containing a list of day values
     */
    public static String dayStringBuilder(String d) throws InvalidDayException{
        
        String[] days = d.split(",");
        String dayNumbers = "";
        
        for (int i = 0; i < days.length; i++) {
            
            String currentDay = days[i].trim();            
            
            if(currentDay.equalsIgnoreCase(MONDAY)){
                dayNumbers += GregorianCalendar.MONDAY;
            }else if(currentDay.equalsIgnoreCase(TUESDAY)){
                dayNumbers += GregorianCalendar.TUESDAY;
            }else if(currentDay.equalsIgnoreCase(WEDNESDAY)){
                dayNumbers += GregorianCalendar.WEDNESDAY;
                
            }else if(currentDay.equalsIgnoreCase(THURSDAY)){
                
                dayNumbers += GregorianCalendar.THURSDAY;
            }else if(currentDay.equalsIgnoreCase(FRIDAY)){
                
                dayNumbers += GregorianCalendar.FRIDAY;
            }else if(currentDay.equalsIgnoreCase(SATURDAY)){
                
                dayNumbers += GregorianCalendar.SATURDAY;
            }else if(currentDay.equalsIgnoreCase(SUNDAY)){
                dayNumbers += GregorianCalendar.SUNDAY;
            } else {
                throw new InvalidDayException("you have specified an invalid day");
            }
            
            if(!(i == days.length - 1)){
                dayNumbers += ",";
            }
            
        }
        
        return dayNumbers;
    }
    
    /**
     * EmailSender Constructor
     * @param pes - a ParticipationEntity containing onformation about the user and the meeting
     * @param msg - a String to define the message to be sent
     */
    public EmailSender(ParticipationEntity[] pes, String msg){
        try {
            // Load Email Server Properties
            loadProperties();
        } catch (IOException ex) {
            //squash already thrown
        }
        Properties props = new Properties();
        props.put("mail.smtp.host", getSMTPServer());
        Session s = Session.getInstance(props);
        // Create new message to be sent
        message = new MimeMessage(s);
        setMessageDetails(pes, msg);
        try {
            setRecipientsTo(participantsToInternetAddress(pes));
        } catch (AddressException ex) {
            ex.printStackTrace(); //TODO
        } catch (MailException ex) {
            ex.printStackTrace();
        }
        
    }
    
    /**
     * participantsToInternetAddress
     * gets email address from ParticipantDTO and creates an InternetAddress
     * @param ParticipantDTO[] of participants to attend them meeting
     * @return InternetAddress[] addresses to be sent the email
     * @throws AddressException address not valid
     */
    private InternetAddress[] participantsToInternetAddress(ParticipationEntity[] participants)throws AddressException{
        InternetAddress[] address = new InternetAddress[participants.length];
        for(int i = 0; i<participants.length; i++){
            try {
                address[i] = new InternetAddress(participants[i].getUserEntity().getEmail());
            } catch (AddressException ex) {
                throw new AddressException("Failed at " + participants[i].getUserEntity().getEmail());
            }
        }
        return address;
    }
    
    /**
     * Creates the html message to be sent in the message body
     */
    public void createInviteMessage(){
        StringBuffer buf = new StringBuffer();
        String room = "TBA";
         if(participation[0].getMeeting().getRoom()!=null){
            if(participation[0].getMeeting().getRoom().getRoomID() !=null){
                room = participation[0].getMeeting().getRoom().getRoomID()
                + " " + participation[0].getMeeting().getRoom().getLocation();
            }
        }
        
        buf.append("<h1>Meeting Request:</h1>"
                + "Meeting:" + participation[0].getMeeting().getTitle() + "<p>"
                + "Description: " + participation[0].getMeeting().getDescription() + "<p>"
                + "Meeting will occur between: " + participation[0].getMeeting().getStartDateRange().getTime().toString() + " and "
                +  participation[0].getMeeting().getEndDateRange().getTime().toString() + "<p>"
                + "Duration of meeting: " + participation[0].getMeeting().getDuration().toString() + "<p>"
                + "Room: " + room);
        
        buf.append("<p><p>" + userMessage);
        buf.append("<p>Follow the link below to confirm your interest:<p>");
        buf.append("Please <a href = \"http://");
        
        String destination = "";
        
        try {
            InetAddress addr = InetAddress.getLocalHost();
            
            //get machines hostname
            destination = addr.getHostName();
            
            if(null == destination ||
                    destination.length() == 0){
                //get machines ip address
                destination = new String(addr.getHostAddress());
            }
            
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        
        
        buf.append(destination);
        buf.append(":8080/se39220GroupB-war/rms/submitResponse?meeting=" + participation[0].getMeeting().getId() +
                "&user=" + participation[0].getUserEntity().getEmail() + "\">Click here</a> to provide your availability between the dates above.<p>");
        
        try {
            setMessageBody(buf.toString());
            setSubject("Invitation To Meeting: " + participation[0].getMeeting().getId());
            send();
        } catch (MailException ex) {
            ex.printStackTrace();
        }
    }
    
    /**
     * Creates and sends the message, for when removing a participant is removed from a meeting
     */
    public void createRemoveMessage(){
        StringBuffer buf = new StringBuffer();
        String room = "TBA";
         if(participation[0].getMeeting().getRoom()!=null){
            if(participation[0].getMeeting().getRoom().getRoomID() !=null){
                room = participation[0].getMeeting().getRoom().getRoomID()
                + " " + participation[0].getMeeting().getRoom().getLocation();
            }
        }
        
        buf.append("<h1>You Have Been REMOVED from the following meeting</h1>"
                + "Meeting:" + participation[0].getMeeting().getTitle() + "<p>"
                + "Description: " + participation[0].getMeeting().getDescription() + "<p>"
                + "Meeting will occur between: " + participation[0].getMeeting().getStartDateRange().getTime().toString() +
                " and " + participation[0].getMeeting().getEndDateRange().getTime().toString() + "<p>"
                + "Duration of meeting: " + participation[0].getMeeting().getDuration().toString() + "<p>"
                + "Room: " + room);
        
        buf.append("<p><p>" + userMessage);
        
        try {
            setMessageBody(buf.toString());
            setSubject("Removed From Meeting");
            send();
        } catch (MailException ex) {
            ex.printStackTrace();
        }
        
    }
    
    /**
     * Creates and sends the message, for when a meeting is confirmed, i.e. the room is booked and the dates set
     */
    public void createConfirmMessage(){
        StringBuffer buf = new StringBuffer();
        String room = "TBA";
        if(participation[0].getMeeting().getRoom()!=null){
            if(participation[0].getMeeting().getRoom() !=null){
                room = participation[0].getMeeting().getRoom().getRoomID()
                + " " + participation[0].getMeeting().getRoom().getLocation();
            }
        }
        
        buf.append("<h1>The following meeting has now been confirmed.</h1>"
                + "Meeting:" + participation[0].getMeeting().getTitle() + "<p>"
                + "Description: " + participation[0].getMeeting().getDescription() + "<p>"
                + "Meeting will occur between: " + participation[0].getMeeting().getStartDateRange().getTime().toString() +
                " and " + participation[0].getMeeting().getEndDateRange().getTime().toString() + "<p>"
                + "Duration of meeting: " + participation[0].getMeeting().getDuration().toString() + "<p>"
                + "Room: " + room);
        
        buf.append("<p><p>" + userMessage);
        
        try {
            setMessageBody(buf.toString());
            setSubject("Meeting Confirmed");
            send();
        } catch (MailException ex) {
            ex.printStackTrace();
        }
        
    }
    
    /**
     * getTimeSent
     * gets the current time
     * @return String of time
     */
    public String getTimeSent() {
        Date time = systemClock;
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_HEADER_PATTERN);
        return sdf.format(time);

    }
    
    /**
     * Sets who the email is from
     * @param from - the address of the person sending the email
     * @throws MailException thrown if there is a problem with the senders address
     */
    public void setFrom(InternetAddress from) throws MailException {
        try {
            message.setFrom(from);
        } catch (MessagingException e) {
            throw new MailException("There was an error setting the From address.", e);
        }
    }
    
    /**
     * Sets in message all the recipients
     * @param recipients array of address of the recipients
     * @throws MailException if their is an error with the recipients
     */
    public void setRecipientsTo(InternetAddress[] recipients) throws MailException {
        String errors = "";
        
        for (InternetAddress address : recipients) {
            try {
                message.addRecipient(Message.RecipientType.TO, address);
            } catch (MessagingException e) {
                if (errors.equals("")) {
                    errors = "The following recipients could not be added: \""
                            + address.getAddress() + "\"";
                } else {
                    errors = errors + ", \"" + address.getAddress() + "\"";
                }
            }
        }
        if (errors.equals("")) {
            // do nothing no errors
        } else {
            throw new MailException(errors);
        }
    }
    
    /**
     * Sets the body of the message
     * @param mess the body of the message as a String
     * @throws MailException if their is an error setting the message
     */
    public void setMessageBody(String mess) throws MailException {
        try {
            message.setContent(mess, "text/html");
        } catch (MessagingException e) {
            throw new MailException("There was an error setting the message body.", e);
        }
    }
    
    /**
     * Sets the subject of the email
     * @param subject the subject String
     * @throws MailException if there is an error setting the subject
     */
    public void setSubject(String subject) throws MailException {
        try {
            message.setSubject(subject);
        } catch (MessagingException e) {
            throw new MailException("There was an error setting the subject.", e);
        }
    }
    
    /**
     * Sends the email
     * @throws MailException if there is an error sending the email
     */
    public void send() throws MailException {
        try {
            //if(!isTesting) {
                message.setFrom(new InternetAddress(participation[0].getMeeting().getInitiator().getEmail()));
            
            
            if (null == message.getRecipients(Message.RecipientType.TO)
            || message.getRecipients(Message.RecipientType.TO).length == 0) {
                throw new MailException("No recipients have been specified.");
            } else if (null == message.getFrom() || message.getFrom().length == 0) {
                throw new MailException("There is no From address set.");
            } else if (null == message.getSubject()
            || message.getSubject().length() == 0) {
                throw new MailException("There is no subject set.");
            } else {
                
                message.setHeader("Date", getTimeSent());
                message.setHeader("X-Mailer", X_MAILER_HEADER);
                Transport.send(message);
            }
            //}
            
        } catch (MessagingException e) {
            throw new MailException("There was an error sending the message.", e);
        }
    }
    
    /**
     * Loads the Email Server propeties
     * @throws IOException if file could not be loaded
     */
    public void loadProperties() throws IOException{
        setSMTPServer(PropertiesManager.getProperty("EMAIL_SMTP_SERVER"));
    }
    
    /**
     * Sets the SMTP server to use
     * @param smtp - a string containing the address of the SMTP server
     */
    private void setSMTPServer(String smtp){
        this.smtpServer = smtp;
    }
    
    /**
     * Gets the address of the SMTP server
     * @return the smtp server
     */
    public String getSMTPServer() {
        return this.smtpServer;
    }
    
    /**
     * Gets the participation details
     * @return ParticipationEntity[] - the participation entities
     */  
    public ParticipationEntity[] getParticipationEntities() {
        
        return participation;
    }
    
    /**
     * Gets the user message
     * @return String - the user message
     */ 
    public String getUserMessage() {
        
        return userMessage;
    }
    
    /**
     * Sets the message details
     * @param pes - a ParticipationEntity containing information about the user and the meeting
     * @param msg - a String containing the message to send
     */
    public void setMessageDetails(ParticipationEntity[] pes, String msg) {
        this.participation = pes;
        this.userMessage = msg;
    }
}
